package com.huanbao.gateway.fliter;

import com.huanbao.common.api.ResultCode;
import com.huanbao.common.exception.GateWayException;
import com.huanbao.gateway.component.CustomRestTemplate;
import com.huanbao.gateway.properties.NotAuthUrlProperties;
import com.huanbao.gateway.util.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.security.PublicKey;
import java.util.List;
import java.util.Map;

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author ：zjq
 * @date ：2020/7/22 0:13
 * @description: TODO  认证过滤器  是否需要和前端约定
 * @version: V1.0
 * @slogan: 天下风云出我辈，一入代码岁月催
 */
@Component
@Slf4j
@EnableConfigurationProperties(value = NotAuthUrlProperties.class)
public class AuthorizationFilter implements GlobalFilter, InitializingBean {


    private PublicKey publicKey;

    @Autowired
    private NotAuthUrlProperties notAuthUrlProperties;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private CustomRestTemplate customRestTemplate;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //  方便注释
        return doFilter(exchange, chain);
//        return chain.filter(exchange);
    }


    public Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain) {
        /**
         * todo 认证逻辑
         */
        String currentUrl = exchange.getRequest().getURI().getPath();

        log.info("网关认证开始URL->:{}", currentUrl);

        // 1：不需要认证的url
        if (shouldSkip(currentUrl)) {
            log.info("跳过认证的URL:{}", currentUrl);
            return chain.filter(exchange);
        }

        //2：获取请求头  value为: “bearer XXXXXXXXXXXXXX”
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");

        //3：请求头为空 抛异常
        if (StringUtils.isEmpty(authHeader)) {
            log.warn("需要认证的url,请求头为空");
            throw new GateWayException(ResultCode.AUTHORIZATION_HEADER_IS_EMPTY);
        }

        //4： 校验jwt 若jwt不对或者超时都会抛出异常
        Claims claims = jwtUtil.validateJwtToken(authHeader, publicKey);

        // 5： 校验 用户 可访问权限(db配置) 因为农污压根就没权限，所以也懒得配置了,这里写死
        hasPremisson(claims, currentUrl);

        // todo 6 : 把从jwt中解析出来的 用户登陆信息存储到请求头中 这里随便放点，实际根据业务来
        ServerWebExchange webExchange = wrapHeader(exchange, claims);

        return chain.filter(exchange);
    }

    /**
     * 包装请求头 根据实际业务需要 放入不敏感的信息  从头中拿用户信息，类似于单体的threadlocal拿，所以可以放入不能修改的，比如loginname
     * 或者你修改完成后重新登陆
     * {@link com.huanbao.auth.component.CustomTokenEnhancer}
     *
     * @param exchange
     * @param claims
     * @return
     */
    private ServerWebExchange wrapHeader(ServerWebExchange exchange, Claims claims) {

        String userId = claims.get("additionalInfo", Map.class).get("userId").toString();
        String loginName = claims.get("additionalInfo", Map.class).get("loginName").toString();
        String useremail = claims.get("additionalInfo", Map.class).get("useremail").toString();


        ServerHttpRequest request = exchange.getRequest().mutate()
                .header("userId", userId)
                .header("loginName", loginName)
                .header("useremail", useremail)
//                .header("menusList", menusList)
                .build();

        return exchange.mutate().request(request).build();

    }

    /**
     * 方法实现说明:不需要授权的路径
     *
     * @param currentUrl 当前路径
     * @return
     */
    private boolean shouldSkip(String currentUrl) {
        //路径匹配器(简介SpringMvc拦截器的匹配器)
        //比如/oauth/** 可以匹配/oauth/token    /oauth/check_token等
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String skipPath : notAuthUrlProperties.getShouldSkipUrls()) {
            if (pathMatcher.match(skipPath, currentUrl)) {
                return true;
            }
        }
        return false;
    }

    private boolean hasPremisson(Claims claims, String currentUrl) {
        boolean hasPremisson = false;
        //登陆用户的权限集合判断
        List<String> premessionList = claims.get("authorities", List.class);
        for (String url : premessionList) {
            if (currentUrl.contains(url)) {
                hasPremisson = true;
                break;
            }
        }
        if (!hasPremisson) {
            log.warn("权限不足");
            throw new GateWayException(ResultCode.FORBIDDEN);
        }

        return hasPremisson;
    }




    /**
     * 初始化 公匙
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // smzl 的方式
        this.publicKey = jwtUtil.getPublicKey(customRestTemplate);


    }

    /**
     *   fox 方式  比较简单 在  RibbonConfig 中 配置
     */
 /*   @Autowired
    private RestTemplate restTemplate;

    @Override
    public void afterPropertiesSet() throws Exception {
        // fox 的 方式
        this.publicKey=jwtUtil.getPublicKey(restTemplate);
    }
*/

}
